/*
 * Copyright 2013 Sigurd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.digiway.rapidbreeze.client.infrastructure.updatable;

import de.digiway.rapidbreeze.shared.rest.IdentifiableObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

/**
 * An abstract {@linkplain ObservableList} implementation which adds update
 * capability to the elements of the list.
 *
 * @author Sigurd
 * @param <S> type of the object which will be used to updat the list
 * @param <T> type of the list itself
 */
public abstract class AbstractUpdatableObservableList<S extends IdentifiableObject, T extends UpdatableEntry> implements ObservableList<T> {

    private ObservableList<T> back = FXCollections.observableArrayList();

    public void updateSingleItem(S toAlign) {
        for (T model : this) {
            if (toAlign.getIdentifier().equals(model.getIdentifier())) {
                model.update(toAlign);
                break;
            }
        }
    }

    public void update(List<S> toAlign) {
        // TODO: This method can be optimized for sure:
        List<S> toAdd = new ArrayList<>();
        List<T> toRemove = new ArrayList<>();

        for (S resource : toAlign) {
            boolean update = false;
            for (T model : this) {
                if (resource.getIdentifier().equals(model.getIdentifier())) {
                    model.update(resource);
                    update = true;
                    break;
                }
            }
            if (!update) {
                toAdd.add(resource);
            }
        }

        for (T model : this) {
            boolean remove = true;
            for (S resource : toAlign) {
                if (resource.getIdentifier().equals(model.getIdentifier())) {
                    remove = false;
                    break;
                }
            }
            if (remove) {
                toRemove.add(model);
            }
        }

        for (S resource : toAdd) {
            addUpdatable(resource);
        }
        for (T bean : toRemove) {
            remove(bean);
        }
    }

    protected abstract void addUpdatable(S toAdd);

    @Override
    public void addListener(ListChangeListener<? super T> ll) {
        back.addListener(ll);
    }

    @Override
    public void removeListener(ListChangeListener<? super T> ll) {
        back.removeListener(ll);
    }

    @Override
    public boolean addAll(T... es) {
        return back.addAll(es);
    }

    @Override
    public boolean setAll(T... es) {
        return back.setAll(es);
    }

    @Override
    public boolean setAll(Collection<? extends T> clctn) {
        return back.setAll(clctn);
    }

    @Override
    public boolean removeAll(T... es) {
        return back.removeAll(es);
    }

    @Override
    public boolean retainAll(T... es) {
        return back.retainAll(es);
    }

    @Override
    public void remove(int i, int i1) {
        back.remove(i, i1);
    }

    @Override
    public int size() {
        return back.size();
    }

    @Override
    public boolean isEmpty() {
        return back.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return back.contains(o);
    }

    @Override
    public Iterator<T> iterator() {
        return back.iterator();
    }

    @Override
    public Object[] toArray() {
        return back.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return back.toArray(a);
    }

    @Override
    public boolean add(T e) {
        return back.add(e);
    }

    @Override
    public boolean remove(Object o) {
        return back.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return back.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        return back.addAll(c);
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        return back.addAll(index, c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return back.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return back.retainAll(c);
    }

    @Override
    public void clear() {
        back.clear();
    }

    @Override
    public T get(int index) {
        return back.get(index);
    }

    @Override
    public T set(int index, T element) {
        return back.set(index, element);
    }

    @Override
    public void add(int index, T element) {
        back.add(index, element);
    }

    @Override
    public T remove(int index) {
        return back.remove(index);
    }

    @Override
    public int indexOf(Object o) {
        return back.indexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
        return back.lastIndexOf(o);
    }

    @Override
    public ListIterator<T> listIterator() {
        return back.listIterator();
    }

    @Override
    public ListIterator<T> listIterator(int index) {
        return back.listIterator(index);
    }

    @Override
    public List<T> subList(int fromIndex, int toIndex) {
        return back.subList(fromIndex, toIndex);
    }

    @Override
    public void addListener(InvalidationListener il) {
        back.addListener(il);
    }

    @Override
    public void removeListener(InvalidationListener il) {
        back.removeListener(il);
    }
}
